//
// class.cs: Class and Struct handlers
//
// Authors: Miguel de Icaza (miguel@gnu.org)
//          Martin Baulig (martin@ximian.com)
//          Marek Safar (marek.safar@gmail.com)
//
// Dual licensed under the terms of the MIT X11 or GNU GPL
//
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
// Copyright 2004-2011 Novell, Inc
// Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
//

using Mono.CompilerServices.SymbolWriter;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using SecurityType = System.Collections.Generic.Dictionary<System.Security.Permissions.SecurityAction, System.Security.PermissionSet>;

namespace Teal.Compiler
{
	//
	// General types container, used as a base class for all constructs which can hold types
	//
    public abstract partial class MemberContainerDefinition : MemberDefinition
	{

        public MemberContainerDefinition() { }

		public readonly MemberKind Kind;

        protected List<MemberContainerDefinition> containers;

		public int CounterAnonymousMethods { get; set; }
		public int CounterAnonymousContainers { get; set; }
		public int CounterSwitchTypes { get; set; }

		public override TypeReference CurrentType {
			get {
				return null;
			}
		}

		public TypeDefinition PartialContainer {
		    get; protected set; }

        public virtual void CreateMetadataName (StringBuilder sb)
		{
			if (container != null && container.MemberName != null)
				container.CreateMetadataName (sb);

			MemberName.CreateMetadataName (sb);
		}

		public virtual void PrepareEmit ()
		{
			if (containers != null) {
				foreach (var t in containers) {
					try {
						t.PrepareEmit ();
					} catch (Exception e) {
						if (MemberName == MemberName.Null)
							throw;

						throw new InternalErrorException (t, e);
					}
				}
			}
		}

		public virtual void EmitContainer ()
		{
			if (containers != null) {
				for (int i = 0; i < containers.Count; ++i)
					containers[i].EmitContainer ();
			}
		}

		public string GetSignatureForMetadata ()
		{
			if (container is TypeDefinition) {
				return container.GetSignatureForMetadata () + "+" + TypeNameParser.Escape (MemberName.Basename);
			}

			var sb = new StringBuilder ();
			CreateMetadataName (sb);
			return sb.ToString ();
		}
    }

	public abstract partial class TypeDefinition : MemberContainerDefinition, ITypeDefinition
	{
	    public TypeDefinition() {}

	    [Flags]
		enum CachedMethods
		{
			Equals				= 1,
			GetHashCode			= 1 << 1,
			HasStaticFieldInitializer	= 1 << 2
		}

        readonly List<MemberDefinition> members2 = new List<MemberDefinition>();

		// Holds a list of fields that have initializers
		protected List<FieldInitializer> initialized_fields;

		// Holds a list of static fields that have initializers
		protected List<FieldInitializer> initialized_static_fields;

		Dictionary<MethodReference, MethodDefinition> hoisted_base_call_proxies;

	    //
		// Points to the first non-static field added to the container.
		//
		// This is an arbitrary choice.  We are interested in looking at _some_ non-static field,
		// and the first one's as good as any.
		//
		protected FieldDefinition first_nonstatic_field;

		//
		// This one is computed after we can distinguish interfaces
		// from classes from the arraylist `type_bases' 
		//

	    protected List<FullNamedExpression> type_bases;

		// Partial parts for classes only

	    GenericTypeParameterBuilder[] all_tp_builders;
		//
		// All recursive type parameters put together sharing same
		// TypeParameter instances
		//
		TypeParameters all_type_parameters;

		public const string DefaultIndexerName = "Item";

		bool has_normal_indexers;
		string indexer_name;
		protected bool requires_delayed_unmanagedtype_check;
		
		protected bool has_static_constructor;

		private CachedMethods cached_method;

		TypeReference current_type;

		public int DynamicSitesCounter;
		public int AnonymousMethodsCounter;
		public int MethodGroupsCounter;

		/// <remarks>
		///  The pending methods that need to be implemented
		///   (interfaces or abstract methods)
		/// </remarks>
		PendingImplementation pending;

		public override TypeReference CurrentType {
			get {
				if (current_type == null) {
					if (IsGenericOrParentIsGeneric) {
						//
						// Switch to inflated version as it's used by all expressions
						//
						var targs = CurrentTypeParameters == null ? TypeReference.EmptyTypes : CurrentTypeParameters.Types;
                        current_type = reference.MakeGenericType(targs);
					} else {
						current_type = reference;
					}
				}

				return current_type;
			}
		}

		public override TypeParameters CurrentTypeParameters {
			get {
				return PartialContainer.MemberName.TypeParameters;
			}
		}

		int CurrentTypeParametersStartIndex {
			get {
				int total = all_tp_builders.Length;
				if (CurrentTypeParameters != null) {
					return total - CurrentTypeParameters.Count;
				}
				return total;
			}
		}

	    PackageDefinition ITypeDefinition.DeclaringAssembly {
			get {
				return containerModule.DeclaringAssembly;
			}
		}

	    public bool HasMembersDefined {
			get {
				return false;
			}
		}

		public bool HasInstanceConstructor {
			get {
				return (caching_flags & Flags2.HasInstanceConstructor) != 0;
			}
			set {
				caching_flags |= Flags2.HasInstanceConstructor;
			}
		}

		// Indicated whether container has StructLayout attribute set Explicit
		public bool HasExplicitLayout {
			get { return (caching_flags & Flags2.HasExplicitLayout) != 0; }
			set { caching_flags |= Flags2.HasExplicitLayout; }
		}

		public bool HasOperators {
			get {
				return (caching_flags & Flags2.HasUserOperators) != 0;
			}
			set {
				caching_flags |= Flags2.HasUserOperators;
			}
		}

		public bool HasStructLayout {
			get { return (caching_flags & Flags2.HasStructLayout) != 0; }
			set { caching_flags |= Flags2.HasStructLayout; }
		}

		public bool IsGenericOrParentIsGeneric {
			get {
				return all_type_parameters != null;
			}
		}

		public bool IsTopLevel {
			get {
				return !(container is TypeDefinition);
			}
		}

		bool ITypeDefinition.IsTypeForwarder {
			get {
				return false;
			}
		}

		bool ITypeDefinition.IsCyclicTypeForwarder {
			get {
				return false;
			}
		}

		//
		// Returns true for secondary partial containers
		//
		bool IsPartialPart {
			get {
				return PartialContainer != this;
			}
		}

		public MemberCache MemberCache {
			get {
				return reference.MemberCache;
			}
		}

		public List<MemberDefinition> Members {
			get {
				return members2;
			}
		}

		string ITypeDefinition.Namespace {
			get {
				var p = container;
				while (p.Kind != MemberKind.Namespace)
					p = p.container;

				return p.MemberName == null ? null : p.GetSignatureForError ();
			}
		}

	    public TypeParameters TypeParametersAll {
			get {
				return all_type_parameters;
			}
		}

	    public int TypeParametersCount {
			get {
			    return reference.genericParameterCount;
			}
		}

		MemberReference.GenericParameter[] ITypeDefinition.TypeParameters {
			get {
				return PartialContainer.CurrentTypeParameters.Types;
			}
		}

		public string GetAttributeDefaultMember ()
		{
			return indexer_name ?? DefaultIndexerName;
		}

		public bool IsComImport {
			get {
				if (attributes == null)
					return false;

				return attributes.Search(containerModule.PredefinedAttributes.ComImport) != null;
			}
		}

		public virtual void RegisterFieldForInitialization (MemberDefinition field, FieldInitializer expression)
		{
			if (IsPartialPart)
				PartialContainer.RegisterFieldForInitialization (field, expression);

			if ((field.ModFlags & Modifiers.@static) != 0){
				if (initialized_static_fields == null) {
					HasStaticFieldInitializer = true;
					initialized_static_fields = new List<FieldInitializer> (4);
				}

				initialized_static_fields.Add (expression);
			} else {
				if (initialized_fields == null)
					initialized_fields = new List<FieldInitializer> (4);

				initialized_fields.Add (expression);
			}
		}

		public void ResolveFieldInitializers (BlockContext ec)
		{
			Debug.Assert (!IsPartialPart);

			if (ec.IsStatic) {
				if (initialized_static_fields == null)
					return;

				bool has_complex_initializer = Compiler.options.optimize == OptimizeLevel.none;
				int i;
				ExpressionStatement [] init = new ExpressionStatement [initialized_static_fields.Count];
				for (i = 0; i < initialized_static_fields.Count; ++i) {
					FieldInitializer fi = initialized_static_fields [i];
					ExpressionStatement s = fi.ResolveStatement (ec);
					if (s == null) {
						s = EmptyExpressionStatement.Instance;
					} else if (!fi.IsSideEffectFree) {
						has_complex_initializer = true;
					}

					init [i] = s;
				}

				for (i = 0; i < initialized_static_fields.Count; ++i) {
					FieldInitializer fi = initialized_static_fields [i];
					//
					// Need special check to not optimize code like this
					// static int a = b = 5;
					// static int b = 0;
					//
					if (!has_complex_initializer && fi.IsDefaultInitializer)
						continue;

					ec.AssignmentInfoOffset += fi.AssignmentOffset;
					ec.CurrentBlock.AddScopeStatement (new StatementExpression (init [i]));
				}

				return;
			}

			if (initialized_fields == null)
				return;

			for (int i = 0; i < initialized_fields.Count; ++i) {
				FieldInitializer fi = initialized_fields [i];

				//
				// Clone before resolving otherwise when field initializer is needed
				// in more than 1 constructor any resolve after the initial one would
				// only took the resolved expression which is problem for expressions
				// that generate extra expressions or code during Resolve phase
				//
				var cloned = fi.Clone (new CloneContext ());

				ExpressionStatement s = fi.ResolveStatement (ec);
				if (s == null) {
					initialized_fields [i] = new FieldInitializer (fi.Field, ErrorExpression.Instance, Location2.Null);
					continue;
				}

				//
				// Field is re-initialized to its default value => removed
				//
				if (fi.IsDefaultInitializer && Compiler.options.optimize != OptimizeLevel.none)
					continue;

				ec.AssignmentInfoOffset += fi.AssignmentOffset;
				ec.CurrentBlock.AddScopeStatement (new StatementExpression (s));
				initialized_fields [i] = (FieldInitializer) cloned;
			}
		}

		public PendingImplementation PendingImplementations {
			get { return pending; }
		}

		public TypeReference GetAttributeCoClass ()
		{
			if (attributes == null)
				return null;

			MemberAnnotation a = attributes.Search (containerModule.PredefinedAttributes.CoClass);
			if (a == null)
				return null;

			return a.GetCoClassAttributeValue ();
		}

		public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa)
		{
			MemberAnnotation a = null;
			if (attributes != null) {
				a = attributes.Search (pa);
			}

			if (a == null)
				return null;

			return a.GetAttributeUsageAttribute ();
		}

	    //
		// Checks that some operators come in pairs:
		//  == and !=
		// > and <
		// >= and <=
		// true and false
		//
		// They are matched based on the return type and the argument types
		//
		void CheckPairedOperators ()
		{
			bool has_equality_or_inequality = false;
			List<OperatorDefinition.OpType> found_matched = new List<OperatorDefinition.OpType> ();

			for (int i = 0; i < members2.Count; ++i) {
				var o_a = members2[i] as OperatorDefinition;
				if (o_a == null)
					continue;

				var o_type = o_a.OperatorType;
				if (o_type == OperatorDefinition.OpType.Equality || o_type == OperatorDefinition.OpType.Inequality)
					has_equality_or_inequality = true;

				if (found_matched.Contains (o_type))
					continue;

				var matching_type = o_a.GetMatchingOperator ();
				if (matching_type == OperatorDefinition.OpType.TOP) {
					continue;
				}

				bool pair_found = false;
				for (int ii = 0; ii < members2.Count; ++ii) {
					var o_b = members2[ii] as OperatorDefinition;
					if (o_b == null || o_b.OperatorType != matching_type)
						continue;

					if (!TypeSpecComparer.IsEqual (o_a.ReturnType, o_b.ReturnType))
						continue;

					if (!TypeSpecComparer.Equals (o_a.ParameterTypes, o_b.ParameterTypes))
						continue;

					found_matched.Add (matching_type);
					pair_found = true;
					break;
				}

				if (!pair_found) {
					Compiler.Report.Error (216, o_a.startLocation,
						"The operator `{0}' requires a matching operator `{1}' to also be defined",
						o_a.GetSignatureForError (), OperatorDefinition.GetName (matching_type));
				}
			}

			if (has_equality_or_inequality) {
				if (!HasEquals)
					Compiler.Report.Warning (660, 2, startLocation2, "`{0}' defines operator == or operator != but does not override Object.Equals(object o)",
						GetSignatureForError ());

				if (!HasGetHashCode)
					Compiler.Report.Warning (661, 2, startLocation2, "`{0}' defines operator == or operator != but does not override Object.GetHashCode()",
						GetSignatureForError ());
			}
		}

		public override void CreateMetadataName (StringBuilder sb)
		{
			if (container.MemberName != null) {
				container.CreateMetadataName (sb);

				if (sb.Length != 0) {
					sb.Append (".");
				}
			}

			sb.Append (MemberName.Basename);
		}
	

		public SourceMethodBuilder CreateMethodSymbolEntry ()
		{
			if (containerModule.DeclaringAssembly.SymbolWriter == null)
				return null;

			var source_file = GetCompilationSourceFile ();
			if (source_file == null)
				return null;

			return new SourceMethodBuilder (source_file.SymbolUnitEntry);
		}

		//
		// Creates a proxy base method call inside this container for hoisted base member calls
		//
		public MethodReference CreateHoistedBaseCallProxy (ResolveContext rc, MethodReference method)
		{
			MethodDefinition proxy_method;

			//
			// One proxy per base method is enough
			//
			if (hoisted_base_call_proxies == null) {
				hoisted_base_call_proxies = new Dictionary<MethodReference, MethodDefinition> ();
				proxy_method = null;
			} else {
				hoisted_base_call_proxies.TryGetValue (method, out proxy_method);
			}

			if (proxy_method == null) {
				string name = CompilerGeneratedContainer.MakeName (method.name, null, "BaseCallProxy", hoisted_base_call_proxies.Count);

				MemberName member_name;
				TypeArguments targs = null;
				TypeReference return_type = method.ReturnType;
				var local_param_types = method.Parameters.Types;

				if (method.IsGeneric) {
					//
					// Copy all base generic method type parameters info
					//
					var hoisted_tparams = method.GenericDefinition.TypeParameters;
					var tparams = new TypeParameters ();

					targs = new TypeArguments ();
					targs.Arguments = new TypeReference[hoisted_tparams.Length];
					for (int i = 0; i < hoisted_tparams.Length; ++i) {
						var tp = hoisted_tparams[i];
						var local_tp = new TypeParameter (tp, null, new MemberName (tp.name, startLocation2), null);
						tparams.Add (local_tp);

						targs.Add (new SimpleName (tp.name, startLocation2));
						targs.Arguments[i] = local_tp.Type;
					}

					member_name = new MemberName (name, tparams, startLocation2);

					//
					// Mutate any method type parameters from original
					// to newly created hoisted version
					//
					var mutator = new TypeParameterMutator (hoisted_tparams, tparams);
					return_type = mutator.Mutate (return_type);
					local_param_types = mutator.Mutate (local_param_types);
				} else {
					member_name = new MemberName (name);
				}

				var base_parameters = new Parameter2[method.Parameters.Count];
				for (int i = 0; i < base_parameters.Length; ++i) {
					var base_param = method.Parameters.FixedParameters[i];
					base_parameters[i] = new Parameter2 (new TypeExpression (local_param_types [i], startLocation2),
						base_param.Name, base_param.ModFlags, null, startLocation2);
					base_parameters[i].Resolve (this, i);
				}

				var cloned_params = ParametersCompiled.CreateFullyResolved (base_parameters, method.Parameters.Types);
				if (method.Parameters.HasArglist) {
					cloned_params.FixedParameters[0] = new Parameter2 (null, "__arglist", Parameter2.Modifier.NONE, null, startLocation2);
					cloned_params.Types[0] = containerModule.PredefinedTypes.RuntimeArgumentHandle.Resolve ();
				}

				// Compiler generated proxy
                proxy_method = new MethodDefinition();

                var block = new ToplevelBlock(proxy_method.ParameterInfo, startLocation2) {
					IsCompilerGenerated = true
				};

				var mg = MethodGroupExpr.CreatePredefined (method, method.containerType, startLocation2);
				mg.InstanceExpression = new BaseLiteral (method.containerType, startLocation2);
				if (targs != null)
					mg.SetTypeArguments (rc, targs);

				// Get all the method parameters and pass them as arguments
				var real_base_call = new Invocation (mg, block.GetAllParametersArguments ());
				Statement statement;
				if (method.ReturnType.Kind == MemberKind.Void)
					statement = new StatementExpression (real_base_call);
				else
					statement = new ReturnStatement (real_base_call, startLocation2);

				block.AddStatement (statement);
				proxy_method.Block = block;

				members2.Add (proxy_method);
				proxy_method.Define ();
				proxy_method.PrepareEmit ();

				hoisted_base_call_proxies.Add (method, proxy_method);
			}

			return proxy_method.Spec;
		}

		public override void PrepareEmit ()
		{
			if ((caching_flags & Flags2.CloseTypeCreated) != 0)
				return;

			foreach (var member in members2) {
                var pbm = member as PropertyOrIndexerDefinition;
				if (pbm != null) {}

			    var pm = member as IParametersMember;
				if (pm != null) {
					var mc = member as MethodOrConstructorDefinition;
					if (mc != null) {
						mc.PrepareEmit ();
					}

					var p = pm.Parameters;
					if (p.IsEmpty)
						continue;

					((ParametersCompiled) p).ResolveDefaultValues (member);
					continue;
				}

				var c = member as FieldDefinition;
                if (member.modifiers.hasFlag(Modifiers.@const)) {
                    var rc = new ResolveContext(c);
                    ((ConstSpec)c.reference).GetConstant(rc);
				}
			}

			base.PrepareEmit ();
		}

		//
		// Replaces normal spec with predefined one when compiling corlib
		// and this type container defines predefined type
		//
		public void SetPredefinedSpec (BuiltinTypeSpec spec)
		{
			// When compiling build-in types we start with two
			// version of same type. One is of BuiltinTypeSpec and
			// second one is ordinary TypeSpec. The unification
			// happens at later stage when we know which type
			// really matches the builtin type signature. However
			// that means TypeSpec create during CreateType of this
			// type has to be replaced with builtin one
			// 
			spec.SetMetaInfo (builder);
			spec.MemberCache = this.reference.MemberCache;
			spec.containerType = this.reference.containerType;

			this.reference = spec;
			current_type = null;
		}

		void ComputeIndexerName ()
		{
			var indexers = MemberCache.FindMembers (reference, MemberCache.IndexerNameAlias, true);
			if (indexers == null)
				return;

			string class_indexer_name = null;

			//
			// Check normal indexers for consistent name, explicit interface implementation
			// indexers are ignored
			//
			foreach (var indexer in indexers) {
				//
				// FindMembers can return unfiltered full hierarchy names
				//
				if (indexer.containerType != reference)
					continue;

				has_normal_indexers = true;

				if (class_indexer_name == null) {
					indexer_name = class_indexer_name = indexer.name;
					continue;
				}

				if (indexer.name != class_indexer_name)
					Compiler.Report.Error (668, ((IndexerDefinition)indexer.MemberDefinition2).startLocation2,
						"Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type");
			}
		}

		void EmitIndexerName ()
		{
			if (!has_normal_indexers)
				return;

			var ctor = containerModule.PredefinedMembers.DefaultMemberAttributeCtor.Get ();
			if (ctor == null)
				return;

			var encoder = new AttributeEncoder ();
			encoder.Encode (GetAttributeDefaultMember ());
			encoder.EncodeEmptyNamedArguments ();

			builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), encoder.ToArray ());
		}

		public override void Emit () {
            if (!has_static_constructor && HasStaticFieldInitializer) {
                var c = DefineDefaultConstructor(true);
                c.Define();
            }

			if (attributes != null)
				attributes.Emit ();

			if (all_tp_builders != null) {
				int current_starts_index = CurrentTypeParametersStartIndex;
				for (int i = 0; i < all_tp_builders.Length; i++) {
					if (i < current_starts_index) {} else {
						var tp = CurrentTypeParameters [i - current_starts_index];
					    tp.Emit ();
					}
				}
			}

			base.Emit ();

			for (int i = 0; i < members2.Count; i++) {
				var m = members2[i];
				if ((m.caching_flags & Flags2.CloseTypeCreated) != 0)
					continue;

				m.Emit ();
			}

			EmitIndexerName ();

			if (pending != null)
				pending.VerifyPendingMethods ();



            if (_declarativeSecurity != null) {
                foreach (var de in _declarativeSecurity) {
                    builder.AddDeclarativeSecurity(de.Key, de.Value);
                }
            }
		}

		public sealed override void EmitContainer ()
		{
			if ((caching_flags & Flags2.CloseTypeCreated) != 0)
				return;

			Emit ();
		}

		/// <summary>
		///   Performs checks for an explicit interface implementation.  First it
		///   checks whether the `interface_type' is a base inteface implementation.
		///   Then it checks whether `name' exists in the interface type.
		/// </summary>
		public bool VerifyImplements (MethodOrPropertyDefinition mb)
		{
			var ifaces = PartialContainer.reference.baseInterfaces;
			if (ifaces != null) {
				foreach (TypeReference t in ifaces){
					if (t == mb.InterfaceType)
						return true;

					var expanded_base = t.baseInterfaces;
					if (expanded_base == null)
						continue;

					foreach (var bt in expanded_base) {
						if (bt == mb.InterfaceType)
							return true;
					}
				}
			}
			
			Compiler.Report.SymbolRelatedToPreviousError (mb.InterfaceType);
			Compiler.Report.Error (540, mb.startLocation2, "`{0}': containing type does not implement interface `{1}'",
				mb.GetSignatureForError (), mb.InterfaceType.GetSignatureForError ());
			return false;
		}

		//
		// Used for visiblity checks to tests whether this definition shares
		// base type baseType, it does member-definition search
		//

	    bool ITypeDefinition.IsInternalAsPublic (PackageDefinition assembly)
		{
			return containerModule.DeclaringAssembly == assembly;
		}

		public virtual bool IsUnmanagedType ()
		{
			return false;
		}

		public void LoadMembers (TypeReference declaringType, bool onlyTypes, ref MemberCache cache)
		{
			throw new NotSupportedException ("Not supported for compiled definition " + GetSignatureForError ());
		}

	    public void Mark_HasEquals ()
		{
			cached_method |= CachedMethods.Equals;
		}

		public void Mark_HasGetHashCode ()
		{
			cached_method |= CachedMethods.GetHashCode;
		}

	    /// <summary>
		/// Method container contains Equals method
		/// </summary>
		public bool HasEquals {
			get {
				return (cached_method & CachedMethods.Equals) != 0;
			}
		}
 
		/// <summary>
		/// Method container contains GetHashCode method
		/// </summary>
		public bool HasGetHashCode {
			get {
				return (cached_method & CachedMethods.GetHashCode) != 0;
			}
		}

		public bool HasStaticFieldInitializer {
			get {
				return (cached_method & CachedMethods.HasStaticFieldInitializer) != 0;
			}
			set {
				if (value)
					cached_method |= CachedMethods.HasStaticFieldInitializer;
				else
					cached_method &= ~CachedMethods.HasStaticFieldInitializer;
			}
		}
         
		public const TypeAttributes staticClassAttribute = TypeAttributes.Abstract | TypeAttributes.Sealed;

		SecurityType _declarativeSecurity;
		protected ConstructorDefinition generatedPrimaryConstructor;
           
		public ToplevelBlock PrimaryConstructorBlock { get; set; }

		public override void ApplyAttributeBuilder (MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa)
		{
			if (a.IsValidSecurityAttribute ()) {
				a.ExtractSecurityPermissionSet (ctor, ref _declarativeSecurity);
				return;
			}

			if (a.Type == pa.StructLayout) {
				PartialContainer.HasStructLayout = true;
				if (a.IsExplicitLayoutKind ())
					PartialContainer.HasExplicitLayout = true;
			}

            //if (a.Type == pa.Dynamic) {
            //    a.Error_MisusedDynamicAttribute ();
            //    return;
            //}

			//   base.ApplyAttributeBuilder (a, ctor, cdata, pa);
		}

		/// <summary>
		/// Defines the default constructors 
		/// </summary>
		protected virtual ConstructorDefinition DefineDefaultConstructor (bool is_static)
		{
			// The default instance constructor is public
			// If the class is abstract, the default constructor is protected
			// The default static constructor is private

			Modifiers mods;
			ParametersCompiled parameters = null;
			if (is_static) {
				mods = Modifiers.@static | Modifiers.@private;
				parameters = ParametersCompiled.EmptyReadOnlyParameters;
			} else {
				mods = ((ModFlags & Modifiers.@abstract) != 0) ? Modifiers.@protected : Modifiers.@public;
				parameters = ParametersCompiled.EmptyReadOnlyParameters;
			}

			var c = new ConstructorDefinition (this, MemberName.Name, mods, null, parameters, startLocation2);
			if (Kind == MemberKind.Class)
				c.Initializer = new GeneratedBaseInitializer (startLocation2, null);

			//AddConstructor (c, true);
			if (PrimaryConstructorBlock == null) {
                c.Block = new ToplevelBlock(parameters, startLocation2) {
					IsCompilerGenerated = true
				};
			} else {
				c.Block = PrimaryConstructorBlock;
			}

			return c;
		}

	}

	public abstract partial class MethodOrPropertyDefinition : TypeMemberDefinition
	{


	    public MethodOrPropertyDefinition() {}

	    //
		// If true, this is an explicit interface implementation
		//

	    protected bool is_external_implementation;

		//
		// The interface type we are explicitly implementing
		//
		public TypeReference InterfaceType;

		//
		// The method we're overriding if this is an override method.
		//
		protected MethodReference base_method;

		readonly Modifiers explicit_mod_flags;
		public MethodAttributes flags;

		protected override bool CheckBase ()
		{
			if ((caching_flags & Flags2.MethodOverloadsExist) != 0) {}

		    if (explicitType != null)
				return true;

			// For System.Object only
			if (containerType.reference.baseClass == null)
				return true;

			MemberReference candidate;
			bool overrides = false;
			var base_member = FindBaseMember (out candidate, ref overrides);

			if ((ModFlags & Modifiers.@override) != 0) {
				if (base_member == null) {
					if (candidate == null) {
						if (this is MethodDefinition && ((MethodDefinition)this).ParameterInfo.IsEmpty && MemberName.Name == DestructorDefinition.MetadataName && MemberName.Arity == 0) {
							Compiler.Report.Error (249, startLocation2, "Do not override `{0}'. Use destructor syntax instead",
								"object.Finalize()");
						} else {
							Compiler.Report.Error (115, startLocation2, "`{0}' is marked as an override but no suitable {1} found to override",
								GetSignatureForError (), SimpleName.GetMemberType (this));
						}
					} else {
						Compiler.Report.SymbolRelatedToPreviousError (candidate);
						if (this is EventDefinition)
							Compiler.Report.Error (72, startLocation2, "`{0}': cannot override because `{1}' is not an event",
								GetSignatureForError (), TypeManager.GetFullNameSignature (candidate));
                        else if (this is PropertyOrIndexerDefinition)
							Compiler.Report.Error (544, startLocation2, "`{0}': cannot override because `{1}' is not a property",
								GetSignatureForError (), TypeManager.GetFullNameSignature (candidate));
						else
							Compiler.Report.Error (505, startLocation2, "`{0}': cannot override because `{1}' is not a method",
								GetSignatureForError (), TypeManager.GetFullNameSignature (candidate));
					}

					return false;
				}

				//
				// Handles ambiguous overrides
				//
				if (candidate != null) {
					Compiler.Report.SymbolRelatedToPreviousError (candidate);
					Compiler.Report.SymbolRelatedToPreviousError (base_member);

					// Get member definition for error reporting
					var m1 = MemberCache.GetMember (base_member.containerType.GetDefinition (), base_member);
					var m2 = MemberCache.GetMember (candidate.containerType.GetDefinition (), candidate);

					Compiler.Report.Error (462, startLocation2,
						"`{0}' cannot override inherited members `{1}' and `{2}' because they have the same signature when used in type `{3}'",
						GetSignatureForError (), m1.GetSignatureForError (), m2.GetSignatureForError (), containerType.GetSignatureForError ());
				}

				ObsoleteAttribute oa = base_member.GetAttributeObsolete ();
				if (oa != null) {
					if (attributes == null || !(attributes.Search(containerModule.PredefinedAttributes.Obsolete) != null)) {
						Compiler.Report.SymbolRelatedToPreviousError (base_member);
						Compiler.Report.Warning (672, 1, startLocation2, "Member `{0}' overrides obsolete member `{1}'. Add the Obsolete attribute to `{0}'",
							GetSignatureForError (), base_member.GetSignatureForError ());
					}
				} else {
					if (attributes != null && attributes.Search(containerModule.PredefinedAttributes.Obsolete) != null) {
						Compiler.Report.SymbolRelatedToPreviousError (base_member);
						Compiler.Report.Warning (809, 1, startLocation2, "Obsolete member `{0}' overrides non-obsolete member `{1}'",
							GetSignatureForError (), base_member.GetSignatureForError ());
					}
				}

				base_method = base_member as MethodReference;
				return true;
			}

			if (base_member == null && candidate != null && (!(candidate is IParametersMember) || !(this is IParametersMember)))
				base_member = candidate;

			if (base_member == null) {
				if ((ModFlags & Modifiers.@new) != 0) {
					if (base_member == null) {
						Compiler.Report.Warning (109, 4, startLocation2, "The member `{0}' does not hide an inherited member. The new keyword is not required",
							GetSignatureForError ());
					}
				}
			} else {
				if ((ModFlags & Modifiers.@new) == 0) {
					ModFlags |= Modifiers.@new;
					if (!IsCompilerGenerated) {
						Compiler.Report.SymbolRelatedToPreviousError (base_member);
						if (!isInterfaceMember && (base_member.modifiers & (Modifiers.@abstract | Modifiers.@virtual | Modifiers.@override)) != 0) {
							Compiler.Report.Warning (114, 2, startLocation2, "`{0}' hides inherited member `{1}'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword",
								GetSignatureForError (), base_member.GetSignatureForError ());
						} else {
							Compiler.Report.Warning (108, 2, startLocation2, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended",
								GetSignatureForError (), base_member.GetSignatureForError ());
						}
					}
				}

				if (!isInterfaceMember && base_member.isAbstract && !overrides && !IsStatic) {
					Compiler.Report.SymbolRelatedToPreviousError (base_member);
					Compiler.Report.Error (533, startLocation2, "`{0}' hides inherited abstract member `{1}'",
						GetSignatureForError (), base_member.GetSignatureForError ());
				}
			}

			return true;
		}

	    public override bool Define ()
		{
			if (isInterfaceMember) {
				ModFlags = Modifiers.@public | Modifiers.@abstract |
					Modifiers.@virtual | (ModFlags & (Modifiers.@new));

				flags = MethodAttributes.Public |
					MethodAttributes.Abstract |
					MethodAttributes.HideBySig |
					MethodAttributes.NewSlot |
					MethodAttributes.Virtual;
			} else {
				// Parent.PartialContainer.MethodModifiersValid (this);

				flags = ModifiersExtensions.MethodAttr (ModFlags);
			}

			if (explicitType != null) {
				InterfaceType = MemberName.ExplicitInterface.ResolveAsType (containerType);
				if (InterfaceType == null)
					return false;

				if (!InterfaceType.IsInterface) {
					Compiler.Report.SymbolRelatedToPreviousError (InterfaceType);
					Compiler.Report.Error (538, startLocation2, "The type `{0}' in explicit interface declaration is not an interface",
						InterfaceType.GetSignatureForError ());
				} else {
					containerType.PartialContainer.VerifyImplements (this);
				}

				ModifiersExtensions.Check (Modifiers.AllowedExplicitImplFlags, explicit_mod_flags, 0, startLocation2, Compiler.Report);
			}

			return base.Define ();
		}

		public override bool EnableOverloadChecks (MemberDefinition overload)
		{
			//
			// Two members can differ in their explicit interface
			// type parameter only
			//
			MethodOrPropertyDefinition imb = overload as MethodOrPropertyDefinition;
			if (imb != null && imb.explicitType != null) {
				if (explicitType != null) {
					caching_flags |= Flags2.MethodOverloadsExist;
				}
				return true;
			}

			return explicitType != null;
		}

	    /// <summary>
		/// Gets base method and its return type
		/// </summary>
		protected virtual MemberReference FindBaseMember (out MemberReference bestCandidate, ref bool overrides)
		{
			return MemberCache.FindBaseMember (this, out bestCandidate, ref overrides);
		}

	    //
		// Returns full metadata method name
		//
		public string GetFullName (MemberName name)
		{
			return GetFullName (name.Name);
		}

		public string GetFullName (string name)
		{
			if (!(explicitType != null))
				return name;

			//
			// When dealing with explicit members a full interface type
			// name is added to member name to avoid possible name conflicts
			//
			// We use CSharpName which gets us full name with benefit of
			// replacing predefined names which saves some space and name
			// is still unique
			//
			return InterfaceType.GetSignatureForError () + "." + name;
		}

		public override bool IsUsed 
		{
			get { return explicitType != null || base.IsUsed; }
		}

	}

	public abstract partial class TypeMemberDefinition : MemberDefinition
	{
		protected FullNamedExpression type_expr;
		protected TypeReference member_type;

	    public TypeMemberDefinition() {}

		public TypeReference MemberType2 {
			get {
				return member_type;
			}
		}

		public FullNamedExpression TypeExpression {
			get {
				return type_expr;
			}
		}

		//
		// Main member define entry
		//
		public override bool Define ()
		{
			//   DoMemberTypeIndependentChecks ();

			//
			// Returns false only when type resolution failed
			//
			if (!resolveTypes2 ())
				return false;

		    return true;
		}

		//
		// Any type_name dependent checks
		//

	    protected virtual bool CheckBase ()
		{
            //CheckProtectedModifier ();

			return true;
		}

		protected virtual bool resolveTypes2 ()
		{
			member_type = type_expr.ResolveAsType (this);
			return member_type != null;
		}

	    public override void ApplyAttributeBuilder(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
            throw new NotImplementedException();
        }

	    public override void defineMember() {
            throw new NotImplementedException();
        }

        public override void resolveMember() {
            throw new NotImplementedException();
        }

        protected override MemberType memberType {
            get { throw new NotImplementedException(); }
        }
    }
}

